﻿using System.ComponentModel;

namespace System
{
    /// <summary>
    /// 包含了与类型相关的一些常用扩展方法
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Never)]
    public static class TypeExtensions
    {
        #region 验证数据类型是否是数值型

        public static bool IsNumericType(this Type type)
        {
            if (type == null)
            {
                return false;
            }

            switch (Type.GetTypeCode(type))
            {
                case TypeCode.Byte:
                case TypeCode.Decimal:
                case TypeCode.Double:
                case TypeCode.Int16:
                case TypeCode.Int32:
                case TypeCode.Int64:
                case TypeCode.SByte:
                case TypeCode.Single:
                case TypeCode.UInt16:
                case TypeCode.UInt32:
                case TypeCode.UInt64:
                    return true;

                case TypeCode.Object:
                    if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
                    {
                        return IsNumericType(Nullable.GetUnderlyingType(type));
                    }
                    return false;
            }
            return false;
        }

        #endregion 验证数据类型是否是数值型

        #region 继承关系
        #region IsAssignableFrom 判断对象是否是指定类型
        //判断两个类型的关系  
        //类型可以是父类，接口  
        //用法：父类.IsAssignableFrom(子类)  
        #endregion //IsAssignableFrom


        #region SubClassOf 判断子类是否继承父类
        //判断两个类型的关系  
        //类型不可以是接口  
        //用法：子类.IsSubClassOf(父类)  

        #endregion //SubClassOf

        #region InstanceOfType 子类对象是否是接口、父类的示例
        //判断对象是否是指定类型  
        //类型可以是父类，接口  
        //用法：父类.IsInstanceOfType(子类对象) 
        #endregion //InstanceOfType

        #endregion //继承关系

        /// <summary>
        /// 	Creates and returns an instance of the desired type
        /// </summary>
        /// <param name = "type">The type to be instanciated.</param>
        /// <param name = "constructorParameters">Optional constructor parameters</param>
        /// <returns>The instanciated object</returns>
        /// <example>
        /// 	<code>
        /// 		var type = Type.GetType(".NET full qualified class Type")
        /// 		var instance = type.CreateInstance();
        /// 	</code>
        /// </example>
        public static object CreateInstance(this Type type, params object[] constructorParameters)
        {
            return CreateInstance<object>(type, constructorParameters);
        }

        /// <summary>
        /// 	Creates and returns an instance of the desired type casted to the generic parameter type T
        /// </summary>
        /// <typeparam name = "T">The data type the instance is casted to.</typeparam>
        /// <param name = "type">The type to be instanciated.</param>
        /// <param name = "constructorParameters">Optional constructor parameters</param>
        /// <returns>The instanciated object</returns>
        /// <example>
        /// 	<code>
        /// 		var type = Type.GetType(".NET full qualified class Type")
        /// 		var instance = type.CreateInstance&lt;IDataType&gt;();
        /// 	</code>
        /// </example>
        public static T CreateInstance<T>(this Type type, params object[] constructorParameters)
        {
            var instance = Activator.CreateInstance(type, constructorParameters);
            return (T)instance;
        }

        ///<summary>
        ///	Check if this is a base type
        ///</summary>
        ///<param name = "type"></param>
        ///<param name = "checkingType"></param>
        ///<returns></returns>
        /// <remarks>
        /// 	Contributed by Michael T, http://about.me/MichaelTran
        /// </remarks>
        public static bool IsBaseType(this Type type, Type checkingType)
        {
            while (type != typeof(object))
            {
                if (type == null)
                    continue;

                if (type == checkingType)
                    return true;

                type = type.BaseType;
            }
            return false;
        }

        ///<summary>
        ///	Check if this is a sub class generic type
        ///</summary>
        ///<param name = "generic"></param>
        ///<param name = "toCheck"></param>
        ///<returns></returns>
        /// <remarks>
        /// 	Contributed by Michael T, http://about.me/MichaelTran
        /// </remarks>
        public static bool IsSubclassOfRawGeneric(this Type generic, Type toCheck)
        {
            while (toCheck != typeof(object))
            {
                if (toCheck == null)
                    continue;

                var cur = toCheck.IsGenericType ? toCheck.GetGenericTypeDefinition() : toCheck;
                if (generic == cur)
                    return true;
                toCheck = toCheck.BaseType;
            }
            return false;
        }

        /// <summary>
        /// Closes the passed generic type with the provided type arguments and returns an instance of the newly constructed type.
        /// </summary>
        /// <typeparam name="T">The typed type to be returned.</typeparam>
        /// <param name="genericType">The open generic type.</param>
        /// <param name="typeArguments">The type arguments to close the generic type.</param>
        /// <returns>An instance of the constructed type casted to T.</returns>
        public static T CreateGenericTypeInstance<T>(this Type genericType, params Type[] typeArguments) where T : class
        {
            var constructedType = genericType.MakeGenericType(typeArguments);
            var instance = Activator.CreateInstance(constructedType);
            return (instance as T);
        } 
    }
}